#if !defined(LOG_TAG)
    #define LOG_TAG                    "ccu"
#endif
#undef LOG_LVL
#if defined(CCU_LOG_LVL)
    #define LOG_LVL                    CCU_LOG_LVL
#endif

#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <termios.h>
#include <stdio.h>
#define __USE_GNU
#include <sched.h>
#include <pthread.h>
#include "ccu.h"

#include "ccu_modules.h"




static int32_t ccu_do_work(struct ccu *pccu)
{
    int32_t ret = 0;

    struct threadpool* pthreadpool = pccu->pthreadpool;

    pthreadpool->op->status(pthreadpool);

    return ret;
}

static int32_t ccu_handle(struct ccu *pccu)
{
    int32_t ret = 0;
    // struct timeval start, end;
    // gettimeofday(&start, NULL);

    ccu_do_work(pccu);

    // gettimeofday(&end, NULL);
    // ccu_proc_time = ((end.tv_sec - start.tv_sec) * 1000000 + (end.tv_usec - start.tv_usec));

    struct timeval tv;
    tv.tv_sec = 0;
    tv.tv_usec = 19000;

    do{
        ret = select(0, NULL, NULL, NULL, &tv);
    }while(ret < 0 && (errno == EINTR));

    // static struct timeval last_time_val = {0};
    // struct timeval time_val;
    // gettimeofday(&time_val, NULL);
    // select_time = (time_val.tv_sec - end.tv_sec) * 1000000 + (time_val.tv_usec - end.tv_usec);
    // total_time = (time_val.tv_sec - start.tv_sec) * 1000000 + (time_val.tv_usec - start.tv_usec);
    // frame_time = (time_val.tv_sec - last_time_val.tv_sec) * 1000000 + (time_val.tv_usec - last_time_val.tv_usec);
    // log_d("*** deal time= %4ld us, select_time= %4ld us, total_time= %4ld us, frame_time= %4ld us ***", ccu_proc_time, select_time, total_time, frame_time);
    // last_time_val = time_val;

    if(pccu->timer_counter++ % 50 == 0){
        pccu->timer_1hz_over = !!!pccu->timer_1hz_over;
    }

    return 0;
}

static int32_t ccu_init(struct ccu* pccu)
{
    int32_t ret = 0;

    INIT_LIST_HEAD(&(pccu->head));
    lock_init(&(pccu->lock));
    lock_init(&(pccu->notify_lock));
    wait_init(&(pccu->wait));

    pccu->enable = 0;

    ret = ccu_create_module(pccu);
    if(ret < 0){
        log_e("create modules error");
        return ret;
    }

    return ret;
}

static int32_t ccu_release(struct ccu* pccu)
{
    ccu_destroy_module(pccu);

    lock_destroy((&pccu->notify_lock));
    lock_destroy((&pccu->lock));
    wait_destroy((&pccu->wait));
    return 0;
}

static int32_t ccu_start(struct ccu* pccu)
{
    int32_t ret = 0;

    ret = ccu_start_module(pccu);
    if(ret < 0){
        log_e("ccu start module error");
        return ret;
    }

    log_i("ccu start successful.");

}

static int32_t ccu_stop(struct ccu* pccu)
{
    int32_t ret = 0;

    ret = ccu_stop_module(pccu);
    if(ret < 0){
        log_e("ccu stop module error");
        return ret;
    }

    return ret;
}

static int32_t ccu_put_frame(struct ccu* pccu, struct list_head* queue, struct ccu_frame* pframe)
{
	lock(&pccu->lock);
	list_add_tail(&pframe->head, queue);
	unlock(&pccu->lock);
	return 0;
}

static struct ccu_frame* ccu_get_frame(struct ccu* pccu, struct list_head* queue)
{
	struct ccu_frame* pframe;
	pframe = NULL;
	lock(&pccu->lock);
	if(!list_empty(queue)){
		pframe = list_first_entry(queue, struct ccu_frame, head);
		list_del(&pframe->head);
	}
	unlock(&pccu->lock);
	return pframe;
}

static int32_t ccu_register_notify(struct ccu* pccu, int32_t event, ccu_event_notify notify, void* object)
{
    struct ccu_event_action* paction;
    if(!notify || (event <= CCU_EVENT_NONE) || (event >= CCU_EVENT_MAX)){
        return -EINVAL;
    }
    paction = (struct ccu_event_action*)malloc(sizeof(struct ccu_event_action));
    if(!paction){
        log_e("malloc error");
        return -ENOMEM;
    }
    paction->notify = notify;
    paction->object = object;
    lock(&(pccu->notify_lock));
    paction->next = pccu->paction[event];
    pccu->paction[event] = paction;
    unlock(&(pccu->notify_lock));
    return 0;
}

static int32_t ccu_unregister_notify(struct ccu* pccu, int32_t event, void* object)
{
    struct ccu_event_action *paction,* ptmp;
    if((event <= CCU_EVENT_NONE) || (event >= CCU_EVENT_MAX)){
        return -EINVAL;
    }
    lock(&(pccu->notify_lock));
    paction = pccu->paction[event];
    if(paction){
        if(paction->object == object){
            pccu->paction[event] = paction->next;
            free(paction);
        }else{
            while(paction->next){
                if(paction->next->object == object){
                    ptmp = paction->next;
                    paction->next = ptmp->next;
                    free(ptmp);
                    break;
                }
                paction = paction->next;
            }
        }
    }
    unlock(&(pccu->notify_lock));

    return 0;
}

static int32_t ccu_trigger_notify(struct ccu* pccu, int32_t event, void* context)
{
    struct ccu_event_action* paction;
    if((event <= CCU_EVENT_NONE) || (event >= CCU_EVENT_MAX)){
        return -EINVAL;
    }
    lock(&(pccu->notify_lock));
    paction = pccu->paction[event];
    while(paction){
        paction->notify(pccu, event, paction->object, context);
        paction = paction->next;
    }
    unlock(&(pccu->notify_lock));

    return 0;
}

static struct ccu_operation ccu_op =
{
    .init = ccu_init,
    .release = ccu_release,

    .start = ccu_start,
    .stop = ccu_stop,

    .handle = ccu_handle,
    .put_frame = ccu_put_frame,
    .get_frame = ccu_get_frame,

    .register_notify = ccu_register_notify,
    .unregister_notify = ccu_unregister_notify,
    .trigger_notify = ccu_trigger_notify,
};

int32_t create_init_ccu(struct ccu** pccu)
{
    int32_t ret;
    struct ccu* ptmp;
    (*pccu) = (struct ccu*)malloc(sizeof(struct ccu));
    if(!(*pccu)){
        log_e("malloc error");
        return -ENOMEM;
    }
    ptmp = *pccu;
    memset(ptmp, 0, sizeof(struct ccu));
    ptmp->op = &ccu_op;
    ret = ptmp->op->init(ptmp);
    if(ret < 0){
        log_e("init error");
        release_destroy_ccu(ptmp);
        return ret;
    }
    return 0;
}

void release_destroy_ccu(struct ccu* pccu)
{
    if(pccu){
        pccu->op->release(pccu);
        free(pccu);
    }
}